using System;
using System.Collections.Generic;
using System.Text;
using Ndo.Text;

namespace Ndo.Maths
{
    public sealed class Complex<TReal> : Number<Complex<TReal>>, IEquatable<Complex<TReal>>
        where TReal : Real<TReal>, new()
    {
        [ParsingPattern(@"({0}(\+)?{1}i)|({1}i)|{0}", "0,0")]
        public Complex(TReal realNumber, TReal imaginaryNumber)
        {
            if (realNumber == null)
                realNumber = Number<TReal>.Zero;
            if (imaginaryNumber == null)
                imaginaryNumber = Number<TReal>.Zero;
            this.realNumber = realNumber;
            this.imaginaryNumber = imaginaryNumber;
        }
        public Complex()
        {
            realNumber = Number<TReal>.One;
            imaginaryNumber = Number<TReal>.Zero;
        }
        private readonly TReal realNumber;
        private readonly TReal imaginaryNumber;
        public TReal RealNumber { get { return realNumber; } }
        public TReal ImaginaryNumber { get { return imaginaryNumber; } }
        public override string ToString()
        {
            string s;
            switch(imaginaryNumber.Sign)
            {
                case SignTypes.Positive:
                    s = imaginaryNumber.ToString();
                    if (s.Length > 0)
                    {
                        if (s[0] != '+')
                            s = "+" + s;
                        s = s + "i";
                        if (realNumber != Number<TReal>.Zero)
                            s = realNumber + s;
                        return s;
                    }
                    break;
                case SignTypes.Negative:
                    s = imaginaryNumber.ToString();
                    if(s.Length>0)
                    {
                        if(s[0] != '-')
                            s = "+" + s;
                        s = s + "i";
                        if (realNumber != Number<TReal>.Zero)
                            s = realNumber + s;
                        return s;
                    }
                    break;
            }
            return realNumber.ToString();
        }
        public static Complex<TReal> operator +(Complex<TReal> c1, Complex<TReal> c2)
        {
            return new Complex<TReal>(c1.realNumber.Add(c2.realNumber), c1.imaginaryNumber.Add(c2.imaginaryNumber));
        }
        public static Complex<TReal> operator -(Complex<TReal> c1, Complex<TReal> c2)
        {
            return new Complex<TReal>(c1.realNumber.SubtractedFrom(c2.realNumber), c1.imaginaryNumber.SubtractedFrom(c2.imaginaryNumber));
        }
        public static Complex<TReal> operator *(Complex<TReal> c1, Complex<TReal> c2)
        {
            TReal a = c1.realNumber, b = c1.imaginaryNumber, c = c2.realNumber, d = c2.imaginaryNumber;
            return new Complex<TReal>(
                a.MultipliedBy(c).SubtractedFrom(b.MultipliedBy(d)),
                a.MultipliedBy(d).Add(b.MultipliedBy(c))
                );
        }
        public static Complex<TReal> operator /(Complex<TReal> c1, Complex<TReal> c2)
        {
            TReal a = c1.realNumber, b = c1.imaginaryNumber, c = c2.realNumber, d = c2.imaginaryNumber, 
                n = c.GetSqr().Add(d.GetSqr());
            return new Complex<TReal>(
                a.MultipliedBy(c).Add(b.MultipliedBy(d)).DividedBy(n),
                a.MultipliedBy(d).SubtractedFrom(b.MultipliedBy(c)).DividedBy(n)
                );
        }

        public static bool operator ==(Complex<TReal> c1, Complex<TReal> c2)
        {
            return c1.Equals(c2);
        }

        public static bool operator !=(Complex<TReal> c1, Complex<TReal> c2)
        {
            return !c1.Equals(c2);
        }

        public override bool Equals(Complex<TReal> other)
        {
            return this.realNumber.Equals(other.realNumber) && this.imaginaryNumber.Equals(other.imaginaryNumber);
        }

        public static implicit operator Complex<TReal>(TReal n)
        {
            return new Complex<TReal>(n, Number<TReal>.Zero);
        }


        public override int GetHashCode()
        {
            return this.realNumber.GetHashCode() ^ this.imaginaryNumber.GetHashCode();
        }
    }
}
